//
// Unihan Input Method
//
// Copyright © 2002-2007 Sun Wah Linux Ltd.
//
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
// USA
//
// $Id: phrase.c 800 2007-07-25 02:52:29Z roger $
//

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include "phrase.h"
#include "core.h"
#include "ui.h"
#include "im_methods.h"

#include <string.h>
#include <stdio.h>
#include <sqlite3.h>

#define UNIHAN_DATA_DIR "/usr/share/unihan2"

/**
 * @defgroup PhraseMatcher Phrase Matcher
 * @{
 */

typedef struct {
	sqlite3 *db;
} PhraseDesktopData;

typedef struct {
	char **result;
	int numCands;
	int luCurrCandOffset;
	int lookupShown;
    char **extraCands;
    int numExtraCands;
    int extraCandsSource;
} PhraseSessionData;

static void _free_extra_cands(PhraseSessionData *ps);

/**
 * @brief Initialises Phrase Matcher
 *
 * @param d     current IMLDesktop
 */
void
phrase_matcher_init(iml_desktop_t *d)
{
	DataPerDesktop *dd = (DataPerDesktop *)d->specific_data;
	PhraseDesktopData *pd = (PhraseDesktopData *)dd->phrase_data;
	if (pd) return;

	pd = (PhraseDesktopData *)malloc(sizeof(PhraseDesktopData));
	memset(pd, 0, sizeof(PhraseDesktopData));
	dd->phrase_data = pd;

	int rc = sqlite3_open(UNIHAN_DATA_DIR "/phrase.db", &(pd->db));
	if (rc) {
		// FIXME: find out what went wrong
		sqlite3_close(pd->db);
		pd->db = NULL;
		return;
	}
}


/**
 * @brief Finalises Phrase Matcher
 *
 * @param d     current IMLDesktop
 */
void
phrase_matcher_done(iml_desktop_t *d)
{
	DataPerDesktop *dd = (DataPerDesktop *)d->specific_data;
	PhraseDesktopData *pd = (PhraseDesktopData *)dd->phrase_data;
	if (!pd) return;

	sqlite3_close(pd->db);

	free(pd);
	dd->phrase_data = NULL;
}


/**
 * @brief Creates Phrase Matcher session context
 *
 * @param s     current IMLSession
 */
void
phrase_matcher_create_session(iml_session_t *s)
{
	DataPerSession *ds = (DataPerSession *)s->specific_data;
	DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;
	PhraseSessionData *ps = (PhraseSessionData *)ds->phrase_data;
	PhraseDesktopData *pd = (PhraseDesktopData *)dd->phrase_data;
	if (!pd || ps) return;
	if (!pd->db) return;

	ps = (PhraseSessionData *)malloc(sizeof(PhraseSessionData));
	memset(ps, 0, sizeof(PhraseSessionData));
	ds->phrase_data = ps;
}


/**
 * @brief Destroys Phrase Matcher session context
 *
 * @param s     current IMLSession
 */
void
phrase_matcher_destroy_session(iml_session_t *s)
{
	DataPerSession *ds = (DataPerSession *)s->specific_data;
	DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;
	PhraseSessionData *ps = (PhraseSessionData *)ds->phrase_data;
	PhraseDesktopData *pd = (PhraseDesktopData *)dd->phrase_data;
	if (!(ps && pd)) return;

	if (ps->result) {
		// FIXME: should use sqlite3_free_table instead
		#if 0
		int i;
		for (i = 0; i < (ps->numCands+1); ++i) {
			free(ps->result[i]);
		}
		free(ps->result);
		#endif
		sqlite3_free_table(ps->result);
		ps->result = NULL;
		ps->numCands = 0;
	}

    _free_extra_cands(ps);

	free(ps);
	ds->phrase_data = NULL;
}


/**
 * @brief Search for phrases that begin with \a utf8
 *
 * @param s     current IMLSession
 * @param utf8  key string in UTF-8
 *
 * @returns the number of phrases matched
 */
int
phrase_matcher_search(iml_session_t *s, const UTF8CHAR *utf8)
{
	DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;
	DataPerSession *ds = (DataPerSession *)s->specific_data;
	PhraseSessionData *ps = (PhraseSessionData *)ds->phrase_data;
	PhraseDesktopData *pd = (PhraseDesktopData *)dd->phrase_data;

	if (!ps) return 0;
	if (!pd->db) return 0;

	// get last char first
	// as it could have come from the results we are about to free
	const UTF8CHAR *char1 = utf8, *char2 = NULL;
	while (char1 && *char1) {
		char2 = char1;
		char1 = utf8_next_char(char1);
	}
	if (!char2 || !(*char2)) return 0;
	char *stmt = s->If->m->iml_new(s, 256);
	stmt[255] = 0;
	snprintf(stmt, 255, "SELECT phrase FROM phrases WHERE key='%s' ORDER BY rank", char2);

	if (ps->result) {
		// FIXME: should use sqlite3_free_table instead
		#if 0
		int i;
		for (i = 0; i < (ps->numCands+1); ++i) {
			free(ps->result[i]);
		}
		free(ps->result);
		#endif
		sqlite3_free_table(ps->result);
		ps->result = NULL;
		ps->numCands = 0;
	}
    _free_extra_cands(ps);

	// FIXME: should not need to malloc ourselves
	#if 0
	ps->result = malloc(sizeof(char *)*(12+1));
	ps->result[0] = strdup("Result");
	int i;
	for (i = 1; i < 13; ++i) {
		ps->result[i] = strdup(key);
	}
	ps->numCands = 12;
	#endif

	sqlite3_get_table(pd->db, stmt, &(ps->result), &(ps->numCands), NULL, NULL);
	ps->luCurrCandOffset = 0;

	return ps->numCands;
}


/**
 * @brief Shows phrase match result lookup
 *
 * @param s     current IMLSession
 */
void
phrase_matcher_show_results(iml_session_t *s)
{
	DataPerSession *ds = (DataPerSession *)s->specific_data;
	PhraseSessionData *ps = (PhraseSessionData *)ds->phrase_data;

	UnihanIM uim;
	uim.privateData = s;

	if (ps->lookupShown == 0) {
		UnihanLookupStart lustart;
		lustart.choicePerWindow = 9;
		lustart.numColumns = 1;
		lustart.numRows = 9;
		lustart.direction = UNIHAN_LOOKUP_DIRECTION_DOWN;
		lookup_start(&uim, &lustart);
		ps->lookupShown = 1;
	}

	UnihanLookupDraw ludraw;
	ludraw.mode = UNIHAN_LOOKUP_MODE_PHRASE;
    ludraw.labelStyles = NULL;
    ludraw.valueStyles = NULL;
    if (ps->luCurrCandOffset >= ps->numCands && ps->numExtraCands > 0) {
        ludraw.numChoices = ps->numExtraCands;
        if (ps->numExtraCands > 9)
            ludraw.numChoices = 9;
        ludraw.currentChoice = ((ps->numCands + 8) / 9) * 9;
        ludraw.title = "Google Suggest";

        ludraw.label = s->If->m->iml_new(s, sizeof(char *) * ludraw.numChoices);
        ludraw.value = s->If->m->iml_new(s, sizeof(char *) * ludraw.numChoices);

        int i;
        for (i = 0; i < ludraw.numChoices; ++i) {
            ludraw.label[i] = s->If->m->iml_new(s, sizeof(char)*8);
            strcpy(ludraw.label[i], "Shift+");
            ludraw.label[i][6] = '1' + i;
            ludraw.label[i][7] = 0;

            ludraw.value[i] = ps->extraCands[i];
        }
    } else {
        if ((ps->luCurrCandOffset / 9) * 9 + 9 >= ps->numCands) {
            ludraw.numChoices = ps->numCands - (ps->luCurrCandOffset / 9) * 9;
        } else
            ludraw.numChoices = 9;
        ludraw.currentChoice = ps->luCurrCandOffset;
        ludraw.title = "";

        ludraw.label = s->If->m->iml_new(s, sizeof(char *) * ludraw.numChoices);
        ludraw.value = s->If->m->iml_new(s, sizeof(char *) * ludraw.numChoices);

        int i;
        for (i = 0; i < ludraw.numChoices; ++i) {
            ludraw.label[i] = s->If->m->iml_new(s, sizeof(char)*8);
            strcpy(ludraw.label[i], "Shift+");
            ludraw.label[i][6] = '1' + i;
            ludraw.label[i][7] = 0;

            ludraw.value[i] = ps->result[ps->luCurrCandOffset + i + 1];
        }
    }
    ludraw.selected = -1;
    if (ps->numExtraCands)
        ludraw.totalChoices = ((ps->numCands + 8) / 9) * 9 + ((ps->numExtraCands > 9)? 9 : ps->numExtraCands);
    else
        ludraw.totalChoices = ps->numCands;

	lookup_draw(&uim, &ludraw);
}


/**
 * @brief Get the phrase match result on row \a row of current page
 *
 * @param s     current IMLSession
 * @param row   the row number of the result
 *
 * @returns the phrase in UTF-8. The returned string is owned by Phrase 
 * Matcher and the caller must not free or modify it.
 */
UTF8CHAR *
phrase_matcher_get_result(iml_session_t *s, int row)
{
	DataPerSession *ds = (DataPerSession *)s->specific_data;
	PhraseSessionData *ps = (PhraseSessionData *)ds->phrase_data;

    if (ps->luCurrCandOffset >= ps->numCands && ps->numExtraCands > 0) {
        if (row >= ps->numExtraCands)
            return NULL;
        return (UTF8CHAR *)ps->extraCands[row];
    } else {
        int offset = ps->luCurrCandOffset + row;
        if (offset >= ps->numCands)
            return NULL;
        return (UTF8CHAR *)ps->result[ps->luCurrCandOffset + row + 1];
    }
}


/**
 * @brief Hides the phrase match result lookup
 *
 * @param s     current IMLSession
 */
void
phrase_matcher_hide_results(iml_session_t *s)
{
	DataPerSession *ds = (DataPerSession *)s->specific_data;
	PhraseSessionData *ps = (PhraseSessionData *)ds->phrase_data;

	UnihanIM uim;
	uim.privateData = s;
	lookup_done(&uim);
	ps->lookupShown = 0;
}


/**
 * @brief Turn to next page of phrase match result lookup
 *
 * @param s     current IMLSession
 */
void
phrase_matcher_next_page(iml_session_t *s)
{
	DataPerSession *ds = (DataPerSession *)s->specific_data;
	PhraseSessionData *ps = (PhraseSessionData *)ds->phrase_data;

	if (ps->luCurrCandOffset >= ps->numCands) {
        ps->luCurrCandOffset = 0;
	} else {
        ps->luCurrCandOffset += 9 - (ps->luCurrCandOffset % 9);
        if (ps->luCurrCandOffset >= ps->numCands) {
            if (ps->numExtraCands == 0)
                ps->luCurrCandOffset = 0;
        }
    }
    phrase_matcher_show_results(s);
}


/**
 * @brief Turn to previous page of phrase match result lookup
 *
 * @param s     current IMLSession
 */
void
phrase_matcher_prev_page(iml_session_t *s)
{
	DataPerSession *ds = (DataPerSession *)s->specific_data;
	PhraseSessionData *ps = (PhraseSessionData *)ds->phrase_data;

	ps->luCurrCandOffset -= 9 + (ps->luCurrCandOffset % 9);
	if (ps->luCurrCandOffset < 0) {
        int offset = ps->numCands % 9;
        if (offset == 0) offset = 9;
        ps->luCurrCandOffset = ps->numCands - offset;
        if (ps->numExtraCands) {
            ps->luCurrCandOffset += 9;
        }
	}
	phrase_matcher_show_results(s);
}

static void
_free_extra_cands(PhraseSessionData *ps)
{
    if (ps->extraCands) {
        int i;
        for (i = 0; i < ps->numExtraCands; ++i) {
            if (ps->extraCands[i])
                free(ps->extraCands[i]);
        }
        free(ps->extraCands);
        ps->extraCands = NULL;
    }
    ps->numExtraCands = 0;
}


/**
 * @brief Adds an extra page of candidates to phrase match lookup.
 *
 * This is currently used by the Google Suggest lookup.
 *
 * @param s          current IMLSession
 * @param cands      array of extra candidates in UTF-8
 * @param num_cands  number of \a cands
 * @param source     the source ID of the extra cands 
 *                   (not used -- only Google Suggest at the moment)
 */
void
phrase_matcher_add_extra_cands(iml_session_t *s, char **cands, int num_cands, int source)
{
	DataPerSession *ds = (DataPerSession *)s->specific_data;
	PhraseSessionData *ps = (PhraseSessionData *)ds->phrase_data;

    _free_extra_cands(ps);

    ps->extraCands = cands;
    ps->numExtraCands = num_cands;
    ps->extraCandsSource = source;

    // FIXME: what to do then?
}

/** @} */
// vi:ts=4:sw=4:nowrap:cin:expandtab
